Ext.Ajax.extraParams = {format:'json'};
/**
 * @class Ext.ux.form.SearchField
 * @alias widget.searchfield
 * @extend Ext.form.field.Trigger
 * @author Kage^no^Neko
 * @version 2.0
 */

/**
 * @todo add remote history support
 */
Ext.define('Ext.ux.form.SearchField', {
    extend: 'Ext.form.field.Trigger',
    alias: 'widget.searchfield',
    paramName : 'query',
    
    /*
     * triggers
     */
    trigger1Cls: Ext.baseCSSPrefix + 'form-clear-trigger',
    trigger2Cls: Ext.baseCSSPrefix + 'form-search-trigger',
    trigger3Cls: Ext.baseCSSPrefix + 'form-arrow-trigger',
    
    /*
     * search history options
     */
    hasSearch : false,
    isHistoryShow: false,
    openCls: Ext.baseCSSPrefix + 'searchhistorylist-open',
    
    initComponent: function(){
        this.callParent(arguments);
        this.on('specialkey', function(f, e){
            if(e.getKey() == e.ENTER){
                this.onTrigger2Click();
            }
        }, this);
    },

    afterRender: function(){
        this.callParent();
        // set clear trigger view
        this.triggerEl.item(0).setDisplayed('none');
        //this.inputEl.findParent('.x-form-trigger-input-cell','.x-form-trigger-input-cell',true).setStyle({float:'left'});
        //this.triggerEl.item(0).findParent('.x-trigger-cell','.x-trigger-cell',true).setStyle({float:'left',marginLeft:'-17px'});
    },
    
    /*
     * triggers actions
     */
    onTrigger1Click : function(){
        var store = this.store,
            proxy = store.getProxy();

        if (this) {
        	this.setValue('');
            proxy.extraParams[this.paramName] = '';
            this.loadResult();
            this.hasSearch = false;
            this.triggerEl.item(0).setDisplayed('none');
            this.doComponentLayout();
        }
    },

    onTrigger2Click : function(){
        var store = this.store,
            proxy = store.getProxy(),
            value = this.getValue();
            
        if (value == null || value.length < 1) {
        	this.onTrigger1Click();
            return;
        }
        proxy.extraParams[this.paramName] = value;
        this.addQueryToHistory(value);
        this.loadResult();
        this.hasSearch = true;
        this.triggerEl.item(0).setDisplayed('block');
        this.doComponentLayout();
    },
    
    onTrigger3Click : function(){
    	if (this.isHistoryShow) {
    		this.hideHistoryList();
        } else {
        	this.showHistoryList();
        }
    },
    
    /*
     * search history funcs
     */
    getHistoryList: function(){
        return this.historyList || (this.historyList = this.createHistoryList());
    },
    
    createHistoryList: function(){
    	var me = this;
    	return Ext.create('Ext.view.BoundList',{
			renderTo: Ext.getBody(),
			width: 100,
			height: 'auto',
			deferEmptyText: false,
			emptyText: 'No history to display',
			displayField: 'searchString',
	        store: Ext.create('Ext.data.ArrayStore', {
	            idIndex: 0,
	            fields: [
	                {name:'searchString',type:'string'}
	            ],
	            data:[]
	        }),
	        itemSelector: '.search-history-item',
	        tpl: [
	            '<tpl for=".">',
	                '<div class="x-boundlist-item search-history-item">{searchString}</div>',
	            '</tpl>'
	        ],
	        floating: true,
	        hidden: true,
	        listeners: {
	            selectionchange: function(dv, nodes ){
	            	if(nodes.length != 0){
                		me.setValue(nodes[0].data.searchString);
                		me.hideHistoryList();
                		this.deselect(nodes);
                	}
	            }
	        }
	    });
    },
    
    alignHistoryList: function() {
        var hl = this.getHistoryList();

        if (this.isHistoryShow) {
        	hl.setWidth(this.bodyEl.getWidth());
            if (hl.isFloating()) {
                this.doAlign();
            }
        }
    },
    
    doAlign: function(){
        var hl = this.historyList,
            aboveSfx = '-above',
            isAbove;

        hl.alignTo(this.inputEl);
        isAbove = hl.el.getY() < this.inputEl.getY();
        this.bodyEl[isAbove ? 'addCls' : 'removeCls'](this.openCls + aboveSfx);
        hl[isAbove ? 'addCls' : 'removeCls'](hl.baseCls + aboveSfx);
    },
    
    showHistoryList: function() {
        var me = this,
        bodyEl, hl, collapseIf;

	    if (!me.isHistoryShow) {
	        bodyEl = me.bodyEl;
	        hl = me.getHistoryList();
	        collapseIf = me.hideHistoryListIf;

	        hl.show();
	        me.isHistoryShow = true;
	        me.alignHistoryList();
	        bodyEl.addCls(me.openCls);

	        me.mon(Ext.getDoc(), {
                mousewheel: collapseIf,
                mousedown: collapseIf,
                scope: me
            });
	        Ext.EventManager.onWindowResize(me.alignHistoryList, me);
	    }
	},
	
	hideHistoryList: function() {
        if (this.isHistoryShow) {
            var me = this,
                openCls = me.openCls,
                hl = me.historyList,
                doc = Ext.getDoc(),
                collapseIf = me.hideHistoryListIf,
                aboveSfx = '-above';

            hl.hide();
            me.isHistoryShow = false;

            me.bodyEl.removeCls([openCls, openCls + aboveSfx]);
            hl.el.removeCls(hl.baseCls + aboveSfx);

            doc.un('mousewheel', collapseIf, me);
            doc.un('mousedown', collapseIf, me);
            Ext.EventManager.removeResizeListener(me.alignHistoryList, me);
        }
    },
    
    hideHistoryListIf: function(e) {
        var me = this;

        if (!e.within(me.triggerEl.item(2), false, true) && !e.within(me.historyList.el, false, true)) {
            me.hideHistoryList();
        }
    },

    /*
     * do query, load result; override this for grid paging toolbar
     */
    loadResult: function(){
    	this.store.loadPage(1);
    },

    /*
     * add query to search history if unique
     */
    addQueryToHistory: function(query){
    	var store = this.getHistoryList().getStore();
    	var find = store.find('searchString',query);
    	if(find == -1){
    		store.add({searchString: query});
    	}
    }
});

/**
 * new form ffield validator - test version
 */

Ext.util.form = {};
Ext.util.form.field = {};
Ext.util.form.field.Validate = {
	/**
	 * add validator function; if exist vtype - return wrapper function based on vtype validator, else if exist self validation function return it, else exception will be throw
	 */
	addValidateFunction: function(type,conf){
		if(typeof Ext.form.field.VTypes[type] == 'function'){
			var v = function(v,f){
				if(!Ext.form.field.VTypes[type](v,f)){
					return Ext.form.field.VTypes[type+'Text'];
				}
				return true;
			};
			return v;
		}else if(typeof this[type+'Validator'] == 'function'){
			return this[type+'Validator'];
		}
		
		throw new Ext.Error({msg:'Unknown validate type'});
	},
	
	identicalValidator: function(value){
		console.log(value,this);
	}
};


/**
 * Form field validator StringLength
 * @vtype strlength
 * @require vtypedata: min/max length
 */
Ext.apply(Ext.form.field.VTypes, {
	strlength:  function(v,f) {
		if(!f.vtypedata || !f.vtypedata.min){
			f.vtypedata = {min:0};
		}
		if(v.length < f.vtypedata.min){
			this.strlengthText = 'Value is less than '+f.vtypedata.min+' characters long';
            return false;
        }
        if (f.vtypedata.max && f.vtypedata.max < v.length) {
        	this.strlengthText = 'Value is more than '+f.vtypedata.max+' characters long';
        	return false;
        }
       	return true;
   	},
   	strlengthText: 'Wrong length'
});
/**
 * Form field validator RegEx
 * @vtype regex
 * @require vtypedata: pattern
 */
Ext.apply(Ext.form.field.VTypes, {
	regex:  function(v,f) {
		if(f.vtypedata && f.vtypedata.test){
	        var status = f.vtypedata.test(v);
	        this.regexText = 'Value does not match against pattern '+f.vtypedata;
	        return status;
		}
        return true;
   	},
   	regexText: 'Value does not match against pattern'
});
/**
 * Form field validator Between/Digits
 * @vtype between
 * @inputmask only digits
 * @require vtypedata: {
 * 				digitsmode,	- boolean, if true other options isn`t need
 * 				inclusive,	- boolean,optional
 * 				min,
 * 				max
 * 			}
 */
Ext.apply(Ext.form.field.VTypes, {
	between:  function(v,f) {
		if(!f.vtypedata || !f.vtypedata.min || !f.vtypedata.max || !f.vtypedata.digitsmode){
			console.log("Missing option. 'min' and 'max' has to be given");
            return true;
		}
		if(f.vtypedata.digitsmode){
			this.betweenText = "Value must contain only digits";
			return /^[0-9]+(\.[0-9]+)?$/.test(v);
		}
		if(f.vtypedata.inclusive){
			if(f.vtypedata.min > v || v > f.vtypedata.max) {
	            this.betweenText = "Value is not between '"+f.vtypedata.min+"' and '"+ f.vtypedata.max+"', inclusively";
	            return false;
	        }
	    }else{
	        if(f.vtypedata.min >= v || v >= f.vtypedata.max){
	          	this.betweenText = "Value is not strictly between '"+f.vtypedata.min+"' and '"+ f.vtypedata.max+"'";
	            return false;
	        }
	    }
        return true;
   	},
   	betweenText: "Value is not correct",
   	betweenMask: /[0-9\.]/
});
/**
 * Form field validator Callback
 * @vtype callback
 * @require vtypedata: {
 * 				callback,		- callback function
 * 				...
 * 				any params		- optional, extra params for callback function
 * 				...
 * 			}
 * @notes callback input args: value,field obj,VTypes obj,extra params arr;
 * 		  function must return boolean true/false
 * 		  validate error msg set to VTypes::callbackText
 */
Ext.apply(Ext.form.field.VTypes, {
	callback:  function(v,f) {
		if(!f.vtypedata || !f.vtypedata.callback || typeof f.vtypedata.callback != 'function'){
			console.log('No callback registered');
			return true;
		}
		var callback = f.vtypedata.callback;
		var params = {};
		for(var i in f.vtypedata){
			if(i != 'callback'){
				params[i] = f.vtypedata[i];
			}
		}
		return callback(v,f,this,params);
   	},
   	callbackText: 'Value is not correct'
});
/**
 * Form field validator Multiple
 * @vtype multiple
 * @require vtypedata: {
 * 				vtypes,			- an array of vtype names
 * 				...
 * 				any params		- params for vtypes (named as vtype name)
 * 				...
 * 			}
 * @notes use for add more than 1 validator to the field
 */
Ext.apply(Ext.form.field.VTypes, {
	multiple:  function(v,f) {
		if(!f.vtypedata || !f.vtypedata.vtypes || !(f.vtypedata.vtypes instanceof Array)){
			console.log('No vtypes found');
			return true;
		}
		var valid = true;
		var vtypes = f.vtypedata.vtypes;
		for(var i in vtypes){
			if(typeof Ext.form.field.VTypes[vtypes[i]] == 'function'){
				/**
				 * @todo rebuild vtypedata format to -> vtypedata{
				 * 										.....
				 * 											<vtype name>:{
				 * 												<params>
				 * 											}
				 * 										.....
				 * 										}
				 */
				valid = Ext.form.field.VTypes[vtypes[i]](v,f);
			}
		}
		return callback(v,f,this,params);
   	},
   	multipleText: 'Value is not correct'
});
/**
 * Form field validator InArray
 * @vtype inarray
 * @require vtypedata: haystack
 */
Ext.apply(Ext.form.field.VTypes, {
	inarray:  function(v,f) {
		if(!f.vtypedata || typeof f.vtypedata != 'object' || !f.vtypedata.indexOf || typeof f.vtypedata.indexOf != 'function'){
			console.log('No haystack registered');
			return true;
		}
        return f.vtypedata.indexOf(v) != -1 ? true : false;
   	},
   	inarrayText: "Value was not found in the haystack"
});
/**
 *
 */
Ext.util.Translator = {
	dictionary: null,
	
	translate: function(textVar,context){
		if(this.dictionary[textVar]){
			if(typeof this.dictionary[textVar] == 'string'){
				textVar = this.dictionary[textVar];
			}else if(context && this.dictionary[textVar][context]){
				textVar = this.dictionary[textVar][context];
			}
		}
		
		return textVar;
	}
};
if(!window._){
	window._ = function(textVar,context){
		return Ext.util.Translator.translate(textVar,context);
	}
}else{
	console.log('Ext Translator Warning > function "_" already exist, can`t create translator function instance!');
}
Ext.util.Helper = {
	displayMessages: function(messages, is_errors, fn, scope, callback_params) {
		var msg = "";
		if (!callback_params) {
			callback_params = [];//IE fix
		}
		if (messages) {
			if (messages instanceof Array) {
				msg = messages.join("<br />");
			} else {
				for(var i in messages) {
					msg += messages[i] + "<br />";
				}
			}
		}
		if (msg != "") {
			var title = "Information";
			if (is_errors == true) {
				title = "Error";
			}
			Ext.MessageBox.alert(title, msg, function(){if (fn) fn.apply(scope, callback_params)});
		} else if (typeof fn == 'function') {
			fn.apply(scope, callback_params);
		}
	},
	
	request: function(url, params, successfn, failfn) {
		var processResponse = function(opt, result, response) {
			if (result == true) {
				var res = Ext.decode(response.responseText);
			} else {
				var res = {
					success: false,
					errors: ['Request is failed.']
				}
			};
			var cb = res.success != true ? failfn : successfn;
			if (!cb) {
				cb = {fn: null, scope: null};
			} else if (typeof cb == 'function') {
				cb = {fn: cb, scope: window};
			} else if (typeof cb.fn != 'function') {
				cb.fn = null;
			};
			var _ps = [res, opt];
			Ext.util.Helper.displayMessages(res.errors, !res.success, cb.fn ? cb.fn : null, cb.scope ? cb.scope : null, _ps);
    	};
    	params = Ext.applyIf(params, {format: 'json'});
		var conf = {
    		method: 'POST',
    		url: url,
    		params: params,
    		callback: processResponse
    	};
    	var Lconnect = new Ext.data.Connection(conf);
    	Lconnect.request(conf);
	},
	
	getScriptParams: function(scriptName, attr){
		var params = null;
		var script = this.findScript(scriptName);
		console.log(script);
		if(script){
			var paramsString = script.getAttribute(attr);console.log('params',paramsString);
			if(paramsString == null){
				paramsString = '';
			}
			if(attr == 'src'){
				var index = paramsString.indexOf('.js?');
				if(index == -1){
					paramsString = '';
				}else{
					paramsString = paramsString.substr(index+4);
				}
			}
			params = Ext.Object.fromQueryString(paramsString);
		}
		
		return params;
	},
	
	findScript: function(scriptName){
		var script = null;
		var reg = new RegExp(scriptName+'\.js$');
		var all = Ext.DomQuery.select('script[src]');
		for(var i in all){
			var src = all[i].getAttribute('src');
			if(src.search(reg) != -1){
				script = all[i];
			}
		}

		return script;
	},
	
	generate_sequences: function(elements,seq_lengths,allowDuplicates,debug){
		var seq_num,
			all_seq_num = 0,
			seq_length,
			divide,
			iter,
			index,
			sequences = [],
			j_fix = 0,
			native_els = elements,
			allowDuplicates = allowDuplicates != undefined ? allowDuplicates:true,
			seq_lengths = (seq_lengths instanceof Array?seq_lengths:[seq_lengths,seq_lengths]);
		
		function factorial_seq_num(el_length,length){
			var seq_n = 1;
			for(var t=0;t<length;t++){
				seq_n *= el_length;
				el_length--;
			}
			
			return seq_n;
		}
		
		function get_elms(elms,exclude){
			var new_elms = [];
			for(var y in elms){
				var del = false;
				for(var x in exclude){
					if(elms[y] == exclude[x]){
						del = true;
					}
				}
				if(!del){
					new_elms.push(elms[y]);
				}
			}
			
			return new_elms;
		}
		
		for(var k=seq_lengths[0];k<seq_lengths[1]+1;k++){
			
			seq_length = k;
			seq_num = allowDuplicates ? Math.pow(native_els.length,seq_length) : (native_els.length >= seq_length ? factorial_seq_num(native_els.length,seq_length) : Math.pow(native_els.length,seq_length));
			all_seq_num += seq_num;			
			
			for(var i=0;i<seq_length;i++){
	
				divide = i == 0 ? seq_num/elements.length : divide/(allowDuplicates ? elements.length: get_elms(native_els,sequences[j_fix]).length);
				
				iter = 1;
				index = 0;
				
				for(var j=0;j<seq_num;j++){
					
					elements = allowDuplicates ? elements: get_elms(native_els,sequences[j+j_fix]);
					
					if(typeof sequences[j+j_fix] == 'undefined')
						sequences[j+j_fix] = [];
					
					sequences[j+j_fix][i] = elements[index];
					
					if(iter%divide == 0){
						index++;
						if(index == elements.length){
							index = 0;
						}
					}
					
					iter++;
				}
				
			}
			
			j_fix += seq_num;
		}
		
		console.log(all_seq_num);
		
		for(var j in sequences){
			sequences[j] = sequences[j].join('');
			if(debug) console.log(j,'--',sequences[j]);
		}
		
		return sequences;
	}
};